package it.niedermann.android.markdown.markwon.textwatcher

import android.text.Editable
import android.text.TextWatcher
import it.niedermann.android.markdown.MarkdownUtil
import it.niedermann.android.markdown.markwon.MarkwonMarkdownEditor
import it.niedermann.android.markdown.model.EListType
import java.util.regex.Pattern

/**
 * Automatically continues lists and checkbox lists when pressing enter
 */
class AutoContinuationTextWatcher(
    originalWatcher: TextWatcher,
    private val editText: MarkwonMarkdownEditor
) : InterceptorTextWatcher(originalWatcher) {
    private var customText: CharSequence? = null
    private var oldText: CharSequence? = null
    private var isInsert = true
    private var sequenceStart = 0
    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        if (count > 0) {
            val inserted = getInsertedString(s, start, before, count)
            if (inserted.isNotEmpty() && inserted[inserted.length - 1] == '\n') {
                handleNewlineInserted(s, start, count)
            }
        }
        oldText = s.toString()
        originalWatcher.onTextChanged(s, start, before, count)
    }

    override fun afterTextChanged(s: Editable) {
        if (customText != null) {
            val customText = customText
            this.customText = null
            if (isInsert) {
                insertCustomText(s, customText)
            } else {
                deleteCustomText(s, customText)
            }
        } else {
            originalWatcher.afterTextChanged(s)
        }
        editText.setMarkdownStringModel(s)
    }

    private fun getInsertedString(
        newText: CharSequence?,
        start: Int,
        before: Int,
        count: Int
    ): CharSequence {
        if (newText != null && newText.length > (if (oldText == null) 0 else oldText!!.length)) {
            // character added
            val position = start + before
            return newText.subSequence(position, position + count - before)
        }
        return ""
    }

    private fun deleteCustomText(s: Editable, customText: CharSequence?) {
        var cutPosition = sequenceStart
        for (i in 0 until customText!!.length) {
            if (oldText!![cutPosition] == customText[i]) {
                cutPosition++
            } else break
        }
        s.replace(sequenceStart, cutPosition + 1, "\n")
        editText.setSelection(sequenceStart + 1)
    }

    private fun insertCustomText(s: Editable, customText: CharSequence?) {
        s.insert(sequenceStart, customText)
    }

    private fun handleNewlineInserted(originalSequence: CharSequence, start: Int, count: Int) {
        val s = originalSequence.subSequence(0, originalSequence.length)
        val startOfLine = MarkdownUtil.getStartOfLine(s, start)
        val line = s.subSequence(startOfLine, MarkdownUtil.getEndOfLine(s, start)).toString()
        val emptyListString = MarkdownUtil.getListItemIfIsEmpty(line)
        if (emptyListString.isPresent) {
            customText = emptyListString.get()
            isInsert = false
            sequenceStart = startOfLine
        } else {
            val builder = StringBuilder()
            val matcher = REGEX_WHITESPACES.matcher(line)
            if (matcher.find()) {
                builder.append(matcher.group())
            }
            val trimmedLine = line.trim { it <= ' ' }
            for (listType in EListType.values()) {
                val isCheckboxList = MarkdownUtil.lineStartsWithCheckbox(trimmedLine, listType)
                val isPlainList =
                    !isCheckboxList && trimmedLine.startsWith(listType.listSymbolWithTrailingSpace)
                if (isPlainList || isCheckboxList) {
                    builder.append(if (isPlainList) listType.listSymbolWithTrailingSpace else listType.checkboxUncheckedWithTrailingSpace)
                    customText = builder
                    isInsert = true
                    sequenceStart = start + count
                    return
                }
            }
            val orderedListNumber = MarkdownUtil.getOrderedListNumber(trimmedLine)
            if (orderedListNumber.isPresent) {
                customText = builder.append(orderedListNumber.get() + 1).append(". ")
                isInsert = true
                sequenceStart = start + count
            }
        }
    }

    companion object {
        private val REGEX_WHITESPACES = Pattern.compile("^\\s*")
    }
}